
package Proyecto;

import java.util.ArrayList;


public class Nodo{
    
    final String estado[][];
    int costoActual;
    String operacionAplicada[];
    /*Toda operacion se define como el conjunto {IdCarro,Direccion del movimiento,Numero de casillas}*/
    Nodo padre;
    final int profundidad;
    
    int posiciones[][]=new int[7][4];
    int posicionesExpansion[][]=new int[7][4];
    boolean asignaciones[];
    String ids[]={"A","B","C","D","E","F","G"};
    Object idsExpansion[][]=new Object[7][2];
    String direciones[]=new String[7];
    String direcionesExpansion[]=new String[7];
    String sentido[]={"Arriba","Abajo","Izquierda","Derecha"};
    ArrayList<Integer[]> obstaculos=new ArrayList<Integer[]>();
    MonticuloBinario nodosExpansion=new MonticuloBinario();
    String[] ordenExpansion=new String[6];
    String [][]resultado=new String[7][7];
    /**
     * 
     * @param estado
     * @param costoActual
     * @param operacionAplicada
     * @param padre
     * @param profundidad 
     */
    public Nodo(String[][] est, int costoActual, String[] operacionAplicada, Nodo padre, int profundidad) {
        this.estado = est;
        this.costoActual = costoActual;
        this.operacionAplicada = operacionAplicada;
        this.padre = padre;
        this.profundidad = profundidad;
        DeterminarPosiciones(estado);        
        
    }
    
//    public Nodo(Object[] elem)
//    {
//       this.estado = (String[][]) elem[0];
//        this.costoActual = (Integer)elem[1];
//        this.operacionAplicada = (String[]) elem[2];
//        this.padre = (Nodo) elem[3];
//        this.profundidad = (Integer)elem[4];
//        DeterminarPosiciones(estado);
//        
//    }
    
     int[][] DeterminarPosiciones(String es [][])
    {
        asignaciones=new boolean[7];
        int i=0;
        while(i<7)
        {
            int j=0;
            while(j<7)
            {
                if(estado[i][j].equals("1"))obstaculos.add(new Integer[]{i,j});
                else{
                    for(int k=0;k<=6;k++)
                    {
                        if(es[i][j].equals(ids[k])&&(!asignaciones[k]))
                        {
                            EvaluarPosicion(es,i,j,k);
                            break;
                        }
                    }
                }
                j++;
            }
            i++;
        }
//        for (int j = 0; j < 7; j++) {
//            int pos=(Integer)nodosExpansion.ExtraerMin()[1];
//            idsExpansion[i]=ids[pos];
//            System.out.println("nodo expansion: "+idsExpansion[j]);
//            
//        }
        DeterminarOrdenExpansion();
        return posiciones;
    }    
    
    void EvaluarPosicion(String e[][],int i,int j,int id)
    {
        posiciones[id][0]=i;
        posiciones[id][1]=j;
        for(int k=2;k>=1;k--)
        {            
            if((j+k<7)&&e[i][j+k].equals(ids[id]))
            {                
                direciones[id]="Horizontal";
                posiciones[id][2]=i;
                posiciones[id][3]=j+k;     
                break;
            }
            else if((i+k<7)&&e[i+k][j].equals(ids[id]))
            {
                direciones[id]="Vertical";
                posiciones[id][2]=i+k;
                posiciones[id][3]=j;      
                break;
            }
        }
        asignaciones[id]=true;
        
    }
    
    public void DeterminarOrdenExpansion()
    {
        direcionesExpansion[0]="Horizontal";
        for(int i=1;i<=6;i++)
        {
           int dif1=Math.abs(posiciones[i][0]-2);
           int dif2=Math.abs(posiciones[i][2]-2);
           if(dif1<dif2)
           {
               nodosExpansion.Ingresar(new Object[]{dif1,i});
           }else nodosExpansion.Ingresar(new Object[]{dif2,i});
        }
        idsExpansion[0]=new Object[]{"A",0};
        posicionesExpansion[0]=posiciones[0];
        for(int j=1;j<=6;j++)
        {
            int pos=(Integer)nodosExpansion.ExtraerMin()[1];
            idsExpansion[j]=new Object[]{ids[pos],pos};
            direcionesExpansion[j]=direciones[pos];
            posicionesExpansion[j]=posiciones[pos];
//            System.out.println("orden expansion :"+idsExpansion[j]+" direccion : "+direcionesExpansion[j+1]);
        }
    }
    
    String [][] mover(int idCarro,String direccion,int casillas)
    {
        String estadoActual[][];
        boolean movValido=true;
        int pos[][]=DeterminarPosiciones(estado);
        int x0=pos[idCarro][0];
        int y0=pos[idCarro][1];
        int x=pos[idCarro][2];
        int y=pos[idCarro][3];
        if(direccion.equals("Arriba"))
        {
           if((x0-casillas)>=0)
           {
               for (int i = x0-1; i >= x0-casillas; i--) {
                   if(!estado[i][y0].equals("0"))
                   {
                       movValido=false;
                       break;
                   }
               }
               if(movValido)
               {
                   pos[idCarro][0]=x0-casillas;
                   pos[idCarro][2]=x-casillas;
               }
 
           }
        }
        else if(direccion.equals("Abajo"))
        {
           if((x+casillas)<=6)
           {
               for (int i = x+1; i <= x+casillas; i++) {
                   if(!estado[i][y0].equals("0"))
                   {
                       movValido=false;
                       break;
                   }
               }
               if(movValido)
               {
                   pos[idCarro][0]=x0+casillas;
                   pos[idCarro][2]=x+casillas;
               }
           }           
        }
        else if(direccion.equals("Izquierda"))
        {
           if((y0-casillas)>=0)
           {
               for (int i = y0-1; i >= y0-casillas; i--) {
                   if(!estado[x0][i].equals("0"))
                   {
                       movValido=false;
                       break;
                   }
               }
               if(movValido)
               {
                   pos[idCarro][1]=y0-casillas;
                   pos[idCarro][3]=y-casillas;
               }
           } 
        }
        else if(direccion.equals("Derecha"))
        {
            if((y+casillas)<=6)
           {
               for (int i = y+1; i <= y+casillas; i++) {
                   if(!estado[x0][i].equals("0"))
                   {
                       movValido=false;
                       break;
                   }
               }
               if(movValido)
               {
                   pos[idCarro][1]=y0+casillas;
                   pos[idCarro][3]=y+casillas;
               }
           }
        }
        if(!movValido){
            return null;
        }else{
            estadoActual= reconstruirMatriz(pos);
        }
        
        return estadoActual;
    }
    String [][] reconstruirMatriz(int[][]pos){
        resultado=new String[7][7];
        for (int i = 0; i < 7; i++) {
            for (int j = 0; j < 7; j++) {
               resultado[i][j]="0";
            }
        }
        for (int i = 0; i < 7; i++) {
            if(direciones[i].equals("Horizontal"))
            {               
                int x=pos[i][0];
                for(int p=pos[i][1];p<=pos[i][3];p++)
                {
                    resultado[x][p]=ids[i];
                }
            }
            else if(direciones[i].equals("Vertical"))
            {
                int y=pos[i][1];
                for(int p=pos[i][0];p<=pos[i][2];p++)
                {
                    resultado[p][y]=ids[i];
                }
            }
        }
        for(int i=0;i<obstaculos.size();i++){
            resultado[obstaculos.get(i)[0]][obstaculos.get(i)[1]]="1";
        }
        return resultado;
    }

    
    
    
    void ImprimirEstado(String[][] est)
    {
        for(int i=0;i<7;i++)
        {
            for(int j=0;j<7;j++)
            {
                System.out.print(est[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println();
    }
    
    void ImprimirPosiciones(int pos[][])
    {
        for(int i=0;i<7;i++)
        {
            for(int j=0;j<4;j++)
            {
                System.out.print(pos[i][j]+" ");
            }
            System.out.println();
        }
    }
    
    public boolean EsMeta()
    {
        int i=posiciones[0][2];
        while((i<7)&&estado[2][i].equals("0"))i++;
       return (i==7);
    }
    
//    public boolean EsMeta()
//    {
//        return (posiciones[0][2]==6);
//    }
    
   
         
    public boolean Viabilidad()
    {
        boolean viabilidad=true;
        int i=posiciones[0][3]+1;
        while(viabilidad&&(i<=6))
        {
            if(estado[2][i].equals("1"))viabilidad=false;
            i++;
        }
        return viabilidad;
    }
    public int calcularHeuristica()
    {
        int i=posiciones[0][3];
        int valorHeuristica=0;
        ArrayList <String>idsCarros=new ArrayList();
        for(int p=0;p<7;p++)
        {
            idsCarros.add(ids[p]);
        }
        while(i<7){
            if(estado[2][i].equals("0"))valorHeuristica+=0.4;
            else if(idsCarros.contains(estado[2][i]))valorHeuristica+=2;
            i++;
        }
        return valorHeuristica;
    }
    

    public ArrayList<Nodo> expandir()
    {
        String estadoActual[][];
        ArrayList<Nodo> arreglo=new ArrayList();        
        int prof=this.profundidad+1;
        int x0,y0,x,y;
        if(!this.EsMeta())
        {
            for(int i=0;i<=6;i++)
            {
                x0=posiciones[i][0];
                y0=posiciones[i][1];
                x=posiciones[i][2];
                y=posiciones[i][3];
              if(direciones[i].equals("Vertical")) 
              {
                  for(int p=1;p<=x0;p++)
                  {
                      estadoActual=mover(i,sentido[0],p);
                      if(estadoActual!=null&&estadoActual!=estado)
                      {                          
                          Nodo hijo=new Nodo(estadoActual,costoActual+p,new String[]{ids[i],sentido[0],String.valueOf(p)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
                  for(int q=5;q>=x;q--)
                  {
                      estadoActual=mover(i,sentido[1],6-q);
                     if(estadoActual!=null&&estadoActual!=estado)
                      {
                          Nodo hijo=new Nodo(estadoActual,costoActual+(6-q),new String[]{ids[i],sentido[1],String.valueOf(6-q)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
              }
              else if(direciones[i].equals("Horizontal")) 
              {
                  for(int p=1;p<=y0;p++)
                  {
                      estadoActual=mover(i,sentido[2],p);
                      if(estadoActual!=null&&estadoActual!=estado)
                      {
                          Nodo hijo=new Nodo(estadoActual,costoActual+p,new String[]{ids[i],sentido[2],String.valueOf(p)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
                  for(int q=5;q>=y;q--)
                  {
                      estadoActual=mover(i,sentido[3],6-q);
                      if(estadoActual!=null&&estadoActual!=estado)
                      {
                          Nodo hijo=new Nodo(estadoActual,costoActual+(6-q),new String[]{ids[i],sentido[3],String.valueOf(6-q)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
              }
            }
        }
        return arreglo;
    }
    
    public ArrayList<Nodo> expandirOrdenExpansion()
    {
        String estadoActual[][];
        ArrayList<Nodo> arreglo=new ArrayList();        
        int prof=this.profundidad+1;
        int x0,y0,x,y;
        if(!this.EsMeta())
        {            
            for(int i=0;i<=6;i++)
            {
                int idCorrespondiente=(Integer)idsExpansion[i][1];
                x0=posiciones[idCorrespondiente][0];
                y0=posiciones[idCorrespondiente][1];
                x=posiciones[idCorrespondiente][2];
                y=posiciones[idCorrespondiente][3];
                if(direciones[idCorrespondiente].equals("Vertical")) 
              {
                  for(int p=1;p<=x0;p++)
                  {
                      estadoActual=mover(idCorrespondiente,sentido[0],p);
                      if(estadoActual!=null&&estadoActual!=estado)
                      {                          
                          Nodo hijo=new Nodo(estadoActual,costoActual+p,new String[]{ids[idCorrespondiente],sentido[0],String.valueOf(p)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
                  for(int q=5;q>=x;q--)
                  {
                      estadoActual=mover(idCorrespondiente,sentido[1],6-q);
                     if(estadoActual!=null&&estadoActual!=estado)
                      {
                          Nodo hijo=new Nodo(estadoActual,costoActual+(6-q),new String[]{ids[idCorrespondiente],sentido[1],String.valueOf(6-q)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
              }
              else if(direciones[idCorrespondiente].equals("Horizontal")) 
              {
                  for(int p=1;p<=y0;p++)
                  {
                      estadoActual=mover(idCorrespondiente,sentido[2],p);
                      if(estadoActual!=null&&estadoActual!=estado)
                      {
                          Nodo hijo=new Nodo(estadoActual,costoActual+p,new String[]{ids[idCorrespondiente],sentido[2],String.valueOf(p)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
                  for(int q=5;q>=y;q--)
                  {
                      estadoActual=mover(idCorrespondiente,sentido[3],6-q);
                      if(estadoActual!=null&&estadoActual!=estado)
                      {
                          Nodo hijo=new Nodo(estadoActual,costoActual+(6-q),new String[]{ids[idCorrespondiente],sentido[3],String.valueOf(6-q)},this,prof);
                          arreglo.add(hijo);
                      }
                  }
              }
            }
        }
        return arreglo;
    }
  
    public ArrayList <String[]> reconstruirSolucion()
    {
        ArrayList <String[]> operacionesAplicadas=new ArrayList<String[]>();   
        Nodo actual=this;
        while(actual.padre!=null)
        {
            operacionesAplicadas.add(0, actual.operacionAplicada);
            actual=actual.padre;
        }
        return operacionesAplicadas;
    }
    
    public void ImprimirHijos(ArrayList<Nodo> hijos)
    {
        for(int i=0;i<hijos.size();i++)
        {
            ImprimirEstado(hijos.get(i).estado);
            System.out.println();
            hijos.get(i).ImprimirPosiciones(hijos.get(i).posiciones);
            System.out.println();
        }
    }
    
    
}